﻿// --------------------------------------------------------------------------------------------------------------------
// <summary>
//   Validator.cs 
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace TRlib.BizUnit.CustomSteps
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using global::BizUnit;

    public class Validator
    {
        private enum MatchType
        {
            Exact,
            ContainsOnly,
            RegEx
        }

        private enum QueryType
        {
            Node,
            Nodes,
            Count
        }
        
        protected void Validate(Stream data, XmlNodeList checks, Context context)
        {
            // Get the xml file into XmlDoc
            XmlDocument doc = new XmlDocument();
            doc.Load(data);

            // Do validations
            foreach (XmlNode xpathCheck in checks)
            {
                // Get xpath query
                var xpathExp = xpathCheck.SelectSingleNode("@query").Value;

                // Derive QueryType
                QueryType queryType = QueryType.Node;
                if (xpathCheck.Attributes["queryType"] != null)
                {
                    switch (xpathCheck.Attributes["queryType"].Value.ToLower())
                    {
                        case "node":
                            queryType = QueryType.Node;
                            break;
                        case "count":
                            queryType = QueryType.Count;
                            break;
                        default:
                            throw new CustomBizUnitExecutionException(String.Format("Illegal value for queryType attribute: {0}", xpathCheck.Attributes["queryType"].Value));
                    }
                }

                // Get the actual results of the query from the xml:
                string actualValue = String.Empty;
                switch (queryType)
                {
                    case QueryType.Node:
                        actualValue = doc.SelectSingleNode(xpathExp).InnerText;
                        break;
                    case QueryType.Count:
                        actualValue = doc.SelectNodes(xpathExp).Count.ToString();
                        break;
                }

                // Decide if this is a xpath check of a "Add to context" instruction:
                if (xpathCheck.Attributes["addToCtx"] != null)
                {
                    if (String.IsNullOrEmpty(actualValue))
                    {
                        throw new CustomBizUnitExecutionException(string.Format("ValidationStep failed for expression {0}, could not add empty or null value to context", xpathExp));
                    }

                    // Add to context
                    string key = xpathCheck.Attributes["addToCtx"].Value;
                    context.Add(key, actualValue, true);

                    context.LogInfo("Successfully added value {0} to context with key {1}. With XPath expression {2}", actualValue, key, xpathExp);
                }
                else
                {
                    // Its a value check. Get expected value
                    var expectedValue = String.Empty;

                    // Derive what type of match is required - defaults to Exact
                    MatchType match = MatchType.Exact;
                    if (xpathCheck.Attributes["matchType"] != null)
                    {
                        switch (xpathCheck.Attributes["matchType"].Value.ToLower())
                        {
                            case "containsonly":
                                match = MatchType.ContainsOnly;
                                break;
                            case "regex":
                                match = MatchType.RegEx;
                                break;
                            case "exact":
                                match = MatchType.Exact;
                                break;
                            default:
                                throw new CustomBizUnitExecutionException(String.Format("Illegal value for matchType attribute: {0}", xpathCheck.Attributes["matchType"].Value));
                        }
                    }

                    // Check if we are getting the expected value from the node or from the context
                    if (xpathCheck.Attributes["takeFromCtx"] != null)
                    {
                        string ctxValue = xpathCheck.Attributes["takeFromCtx"].Value;
                        expectedValue = context.GetValue(ctxValue);
                    }
                    else
                    {
                        expectedValue = xpathCheck.SelectSingleNode(".").InnerText;
                    }

                    context.LogInfo("ValidationStep evaluting XPath {0} against \"{1}\" using MatchType {2}", xpathExp, expectedValue, match);

                    // Check the expected value against the actual value.
                    if (match == MatchType.Exact && expectedValue.Trim() == actualValue.Trim())
                    {
                        context.LogInfo("Successfully evaluated XPath {0}", xpathExp);
                    }
                    else if (match == MatchType.ContainsOnly && actualValue.Trim().Contains(expectedValue.Trim()))
                    {
                        context.LogInfo("Successfully evaluated XPath {0}", xpathExp);
                    }
                    else if (match == MatchType.RegEx)
                    {
                        throw new NotImplementedException("Validate method does not have an implementation for evaluating RegEx expressions. Please add this if needed.");
                    }
                    else
                    {
                        throw new CustomBizUnitExecutionException(string.Format("ValidationStep failed: compare {0} against {1} with MatchType {2}, xpath query used: {3}", expectedValue, actualValue, match, xpathExp));
                    }
                }
            }
        }
    }
}
